home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / TEMPLDEF.PAK / TEMPLDEF.CPP < prev   
C/C++ Source or Header  |  1997-05-06  |  14KB  |  599 lines

  1. // templdef.cpp : A simple C++ template expansion utility.
  2. //
  3. //   This utility takes a template file which uses a syntax
  4. //   subset similar to the proposed C++ template syntax
  5. //   and expands it into the equivalent non-template C++ syntax.
  6. //
  7. //   Note that this is intended as an example program, and
  8. //   handles only a specific syntax in the template files.
  9. //
  10. // This is a part of the Microsoft Foundation Classes C++ library.
  11. // Copyright (C) 1992-1995 Microsoft Corporation
  12. // All rights reserved.
  13. //
  14. // This source code is only intended as a supplement to the
  15. // Microsoft Foundation Classes Reference and related
  16. // electronic documentation provided with the library.
  17. // See these sources for detailed information regarding the
  18. // Microsoft Foundation Classes product.
  19.  
  20. #include <ctype.h>
  21. #include <afxcoll.h>
  22.  
  23. #define SIGNON_VER 3
  24. #define SIGNON_REV 0
  25.  
  26. #define istringTypeParamMax 10
  27.  
  28. CString strHFileName;
  29. CString strInlFileName;
  30. CString strCppFileName;
  31. CString strCttFileName;
  32.  
  33. CString stringTemplate = "template";
  34. CString stringClass = "class";
  35. CString stringTemplateName;
  36. CString stringTypedefName;
  37.  
  38. const char chMoreThanOne = '\0';
  39. const char chNot = '\0';
  40.  
  41. /////////////////////////////////////////////////////////////////////////////
  42.  
  43. void Error(const char* szErrorMessage, const char* szErrorParam = NULL)
  44. {
  45.     fprintf(stderr, "templdef : error: ");
  46.     fprintf(stderr, szErrorMessage, szErrorParam);
  47.     fprintf(stderr, ".\n");
  48.     exit(1);
  49. }
  50.  
  51. void PrintUsage()
  52. {
  53.     fprintf(stderr, "templdef usage (one line):\n\n"
  54.         "  templdef [/Dsymbol]... \"CTemplateName<arg1, arg2> CClassName\"\n"
  55.         "\tinfile.ctt outfile.h outfile.inl outfile.cpp\n"
  56.         "\n"
  57.         "  Multiple /D switches may be used to define symbols processed by $ifdef.\n"
  58.         "\n"
  59.         "  This command expands a template class to be equivalent to:\n"
  60.         "\n"
  61.         "\ttypedef CTemplateName<arg1, arg2> CClassName;\n");
  62. }
  63.  
  64. void CheckSyntax(BOOL fGoodSyntax, const char* sz)
  65. {
  66.     if (!fGoodSyntax)
  67.         Error("expecting %s", sz);
  68. }
  69.  
  70. /////////////////////////////////////////////////////////////////////////////
  71. // CTokenFile
  72. // For the purpose of reading source code, this sort of CStdioFile derivative
  73. // proves quite handy.  This could be made really powerful, but this is a
  74. // start.
  75.  
  76. class CTokenFile : public CStdioFile
  77. {
  78. private:
  79.     enum { ichMax = 1024 };
  80.     char ach[ichMax];
  81.     int ich;
  82.     static CString stringToken;
  83.     BOOL fComment;
  84.     void GetBuf();
  85.     char GetChar() { if (ach[ich] == chNot) GetBuf(); return ach[ich++]; }
  86.     void UnGetChar() { --ich; } // note: really a single char pushback
  87. public:
  88.     char PeekChar() { return ach[ich]; }
  89.     void InitBuf(const char* sz);
  90.     static CString& TokenString() { return stringToken; }
  91.     CTokenFile();
  92.     char GetToken();
  93.     char GetPrintToken();
  94.     char GetTypeToken();
  95.     char GetNameToken();
  96.     void PutToken(char ch) { fputc(ch, m_pStream); }
  97.     void PutToken(const CString& string) { WriteString(string); }
  98.     void PutToken() { WriteString(stringToken); }
  99.     BOOL AtFileEnd() { return ach[0] == chNot; }
  100.     BOOL AtBufEnd() { return ach[ich] == chNot; }
  101.     void SafeOpen(const CString& string, UINT nStyleFlags);
  102. };
  103. CString CTokenFile::stringToken;
  104.  
  105. CTokenFile::CTokenFile()
  106. {
  107.     ach[0] = '\n';
  108.     ach[1] = chNot;
  109.     ich = 1;
  110.     fComment = FALSE;
  111. };
  112.  
  113. void CTokenFile::GetBuf()
  114. {
  115.     if (ReadString(ach, ichMax) == NULL)
  116.         ach[0] = chNot;
  117.  
  118.     ich = 0;
  119. }
  120.  
  121. void CTokenFile::InitBuf(const char* sz)
  122. {
  123.     strncpy(ach, sz, ichMax);
  124.     ich = 0;
  125. }
  126.  
  127. void CTokenFile::SafeOpen(const CString& string, UINT nStyleFlags)
  128. {
  129.     BOOL fSuccess = Open(string, nStyleFlags, 0);
  130.  
  131.     if (!fSuccess)
  132.         Error("can't open file \"%s\"", string);
  133. }
  134.  
  135. char CTokenFile::GetToken()
  136. {
  137.     if (AtBufEnd())
  138.         GetBuf();
  139.  
  140.     if (AtFileEnd())
  141.         exit(0);
  142.  
  143.     fComment = FALSE;
  144.  
  145.     char ch = GetChar();
  146.     char* pch = stringToken.GetBuffer(1024);
  147.     char* pchInit = pch;
  148.     *pch++ = ch;
  149.     *pch = '\0';
  150.  
  151.     // assuming this doesn't "really" release the buffer!
  152.     stringToken.ReleaseBuffer(1);
  153.  
  154.     if (!isalnum(ch) &&
  155.         ch != '/'    &&
  156.         ch != '_'    &&
  157.         ch != '\''   &&
  158.         ch != '"'    &&
  159.         ch != '#'    &&
  160.         ch != '$')
  161.     {
  162.         return ch;
  163.     }
  164.  
  165.     if ((ch == '\'') || (ch == '"'))
  166.     {
  167.         char ch2;
  168.         while ((ch2 = GetChar()) != ch)
  169.         {
  170.             *pch++ = ch2;
  171.         }
  172.         *pch++ = ch2;
  173.         stringToken.ReleaseBuffer(pch - pchInit);
  174.         return chMoreThanOne;
  175.     }
  176.  
  177.     if (ch == '/')
  178.     {
  179.         char ch2 = GetChar();
  180.         if (AtFileEnd()) exit(0);
  181.         if (ch2 == '/')
  182.         {
  183.             char ch3 = GetChar();
  184.             if (ch3 != '$')
  185.             {
  186.                 UnGetChar();
  187.                 *pch++ = '/';
  188.                 fComment = TRUE;
  189.                 while ((ch3 = GetChar()) != '\n')
  190.                     *pch++ = ch3;
  191.             }
  192.             else
  193.             {
  194.                 stringToken.ReleaseBuffer(0);
  195.             }
  196.             *pch++ = '\n';
  197.             stringToken.ReleaseBuffer(pch - pchInit);
  198.             return chMoreThanOne;
  199.         }
  200.         else if (ch2 == '*')
  201.         {
  202.             char ch3 = chNot;
  203.             char ch4 = chNot;
  204.             *pch++ = '*';
  205.             fComment = TRUE;
  206.             while (ch4 != '/')
  207.             {
  208.                 while ((ch3 = GetChar()) != '*')
  209.                     *pch++ = ch3;
  210.                 *pch++ = '*';
  211.  
  212.                 ch4 = GetChar();
  213.                 if (ch4 != '/')
  214.                     UnGetChar();
  215.             }
  216.             *pch++ = '/';
  217.             stringToken.ReleaseBuffer(pch - pchInit);
  218.             return chMoreThanOne;
  219.         }
  220.         else
  221.         {
  222.             UnGetChar();
  223.             return ch;
  224.         }
  225.     }
  226.  
  227.     if (isdigit(ch))
  228.     {
  229.         char ch2;
  230.         ch2 = GetChar();
  231.         if (!isalpha(ch2))
  232.         {
  233.             UnGetChar();
  234.             return ch;
  235.         }
  236.  
  237.         *pch++ = ch2;
  238.         stringToken.ReleaseBuffer(pch - pchInit);
  239.         return chMoreThanOne;
  240.     }
  241.  
  242.     while ((!AtFileEnd()) &&
  243.         (ch = GetChar(), (isalnum(ch) || (ch == '_'))))
  244.     {
  245.         *pch++ = ch;
  246.     }
  247.  
  248.     if (!AtFileEnd())
  249.         UnGetChar();
  250.  
  251.     stringToken.ReleaseBuffer(pch - pchInit);
  252.     return chMoreThanOne;
  253. }
  254.  
  255. char CTokenFile::GetPrintToken()
  256. {
  257.     char chToken;
  258.     while (((chToken = GetToken()) != chMoreThanOne) &&
  259.            (isspace(chToken)) || fComment)
  260.         /* try again */ ;
  261.  
  262.     return chToken;
  263. }
  264.  
  265. char CTokenFile::GetTypeToken()
  266. {
  267.     char chToken = GetPrintToken();
  268.     CString typeStr = stringToken;
  269.     char chFirst = typeStr[0];
  270.  
  271.     CheckSyntax(
  272.         isalnum(chFirst) ||
  273.         chFirst == '_'   ||
  274.         chFirst == '\''  ||
  275.         chFirst == '"',
  276.         "a type description or constant starting with an\n"
  277.         "alphanumeric or an underbar, or a string or char constant");
  278.  
  279.     while ((chToken = GetPrintToken()) != ',' && chToken != '>')
  280.     {
  281.         if (chToken != '*' && chToken != '&' && !isdigit(chToken))
  282.             typeStr += ' ';
  283.  
  284.         typeStr += stringToken;
  285.     }
  286.  
  287.     stringToken = typeStr;
  288.     return chToken;
  289. }
  290.  
  291. char CTokenFile::GetNameToken()
  292. {
  293.     GetPrintToken();
  294.  
  295.     CheckSyntax((isalpha(stringToken[0]) || stringToken[0] == '_'),
  296.         "a name token starting in an alpha char or underbar");
  297.  
  298.     int l = stringToken.GetLength();
  299.     for (int i=1; i<l; ++i)
  300.     {
  301.         CheckSyntax(isalnum(stringToken[i]) || stringToken[i] == '_',
  302.             "a name token consisting of alphanumerics or underbars");
  303.     }
  304.  
  305.     CheckSyntax(stringToken != stringTemplate, "a name token");
  306.     CheckSyntax(stringToken != stringClass, "a name token");
  307.  
  308.     return chMoreThanOne;
  309. }
  310.  
  311. /////////////////////////////////////////////////////////////////////////////
  312.  
  313. CMapStringToString map;
  314.  
  315. BOOL isTailMatch(CString string, CString tail)
  316. {
  317.     BOOL retval = string.Right(tail.GetLength()) == tail;
  318.     ASSERT(retval);
  319.     return retval;
  320. }
  321.  
  322. CTokenFile fileH;
  323. CTokenFile fileInl;
  324. CTokenFile fileCpp;
  325. CTokenFile fileCtt;
  326.  
  327. // TranslateTo:
  328. // Copy fileCtt to fileOut, translating words as necessary.
  329. // On the way, swallow things outside of blocks and after
  330. // the "template" word inside of '<' '>' brackets.
  331. // Return if "//$" comment matches pszEnd string
  332. //
  333. // Also, on the way, any $ifdef or $else statements will be
  334. // swallowed as appropriate.
  335. //
  336. void TranslateTo(CTokenFile& fileOut, const char* pszEnd)
  337. {
  338.     char chToken;
  339.     static BOOL fSwallowParams = TRUE;
  340.     BOOL fSwallow = FALSE;
  341.     BOOL finIf = FALSE;
  342.     BOOL finElse = FALSE;
  343.     BOOL fSwallowIf = FALSE;
  344.     BOOL fSwallowElse = FALSE;
  345.     CString stringTokenOut;
  346.  
  347.     while ((chToken = fileCtt.GetToken()) != chMoreThanOne ||
  348.             (!fileCtt.TokenString().IsEmpty() && fileCtt.TokenString() != pszEnd))
  349.     {
  350.         if (chToken != chMoreThanOne)
  351.         {
  352.             if (chToken == '{')         /* match } */
  353.                 fSwallowParams = FALSE;
  354.             if (fSwallowParams && (chToken == '<'))
  355.             {
  356.                 while (chToken != '>')
  357.                     chToken = fileCtt.GetToken();
  358.                 // if template is at end of line eat end of line as well
  359.                 if (fileCtt.PeekChar() == '\n')
  360.                     fileCtt.GetToken(); // skip the '\n'
  361.             }
  362.             else
  363.             {
  364.                 if (!fSwallow)
  365.                     fileOut.PutToken();
  366.             }
  367.         }
  368.         else
  369.         {
  370.             if (fileCtt.TokenString()[0] == '$')
  371.             {
  372.                 if (fileCtt.TokenString() == "$ifdef")
  373.                 {
  374.                     fileCtt.GetPrintToken();
  375.                     if (map.Lookup(fileCtt.TokenString(), stringTokenOut))
  376.                     {
  377.                         fSwallowElse = TRUE;
  378.                         fSwallowIf = FALSE;
  379.                     }
  380.                     else
  381.                     {
  382.                         fSwallowIf = TRUE;
  383.                         fSwallowElse = FALSE;
  384.                     }
  385.  
  386.                     if (fSwallowIf || fSwallowElse)
  387.                     {
  388.                         fileCtt.TokenString() = "";
  389.                         finIf = TRUE;
  390.                     }
  391.                     fSwallow = fSwallowIf;
  392.                 }
  393.                 else if (fileCtt.TokenString() == "$else")
  394.                 {
  395.                     if (finIf)
  396.                     {
  397.                         fileCtt.TokenString() = "";
  398.                         finIf = FALSE;
  399.                         finElse = TRUE;
  400.                     }
  401.                     fSwallow = fSwallowElse;
  402.                 }
  403.                 else if (fileCtt.TokenString() == "$endif")
  404.                 {
  405.                     if (finIf || finElse)
  406.                     {
  407.                         fileCtt.TokenString() = "";
  408.  
  409.                         // eat any evil junk after the $endif
  410.                         while ((chToken = fileCtt.GetToken()) != '\n' &&
  411.                             fileCtt.TokenString()[0] != '/')
  412.                             /* spin */ ;
  413.  
  414.                         if (chToken != '\n')
  415.                             fileCtt.GetToken();
  416.                     }
  417.                     finIf = FALSE;
  418.                     finElse = FALSE;
  419.                     fSwallowIf = FALSE;
  420.                     fSwallowElse = FALSE;
  421.                     fSwallow = FALSE;
  422.                 }
  423.             }
  424.  
  425.             if (fileCtt.TokenString() == stringTemplate)
  426.                 fSwallowParams = TRUE;
  427.  
  428.             if (map.Lookup(fileCtt.TokenString(), stringTokenOut))
  429.             {
  430.                 if (!fSwallow)
  431.                     fileOut.PutToken(stringTokenOut);
  432.             }
  433.             else
  434.             {
  435.                 if (!fSwallow)
  436.                     fileOut.PutToken();
  437.             }
  438.         }
  439.     }
  440. }
  441.  
  442. // main:
  443. // Gets the arguments, checks them, then processes the files.
  444. //
  445. main(int argc, char* argv[])
  446. {
  447.     fprintf(stderr,
  448.         "\nMicrosoft (R) C++ Template Definition Utility   Version %d.%02d\n"
  449.         "Copyright (c) Microsoft Corp. 1992-1995. All rights reserved.\n\n",
  450.         SIGNON_VER, SIGNON_REV);
  451.  
  452.     // Process any switches
  453.     //
  454.     for (int i=1; i<(argc-3) && argv[i][0] == '/'; ++i)
  455.     {
  456.         switch (argv[i][1])
  457.         {
  458.         case 'D':
  459.             if (argv[i][2] != '\0')
  460.                 map.SetAt(&argv[i][2], "1");    // define the symbol as a 1
  461.             break;
  462.         default:
  463.             PrintUsage();
  464.             exit(1);
  465.         }
  466.     }
  467.  
  468.     // Process template creation arguments
  469.     //
  470.     CString stringTypes;
  471.     for (/*null*/; i<(argc-3); ++i)
  472.         stringTypes += CString(argv[i]) + ' ';
  473.  
  474.     if (argc < 5)
  475.     {
  476.         PrintUsage();
  477.         exit(1);
  478.     }
  479.  
  480.     // Copy the template, header and source file name arguments.
  481.     //
  482.     strCttFileName = argv[argc-4];
  483.     strHFileName   = argv[argc-3];
  484.     strInlFileName = argv[argc-2];
  485.     strCppFileName = argv[argc-1];
  486.  
  487.     // Check to make sure that the args are in the right order.
  488.     if (!isTailMatch(strCttFileName, ".ctt"))
  489.         Error("template file should have a .ctt extension");
  490.  
  491.     if (!isTailMatch(strCppFileName, ".cpp") &&
  492.         !isTailMatch(strCppFileName, ".cxx"))
  493.         Error("module file should have a .cpp or .cxx extension");
  494.  
  495.     if (!isTailMatch(strInlFileName, ".inl"))
  496.         Error("inline file should have an .inl extension");
  497.  
  498.     if (!isTailMatch(strHFileName, ".h") &&
  499.         !isTailMatch(strHFileName, ".hpp") &&
  500.         !isTailMatch(strHFileName, ".hxx"))
  501.         Error("header file should have an .h, .hpp or .hxx extension");
  502.  
  503.     // Open the files.
  504.     //
  505.     fileCtt.SafeOpen(strCttFileName, CTokenFile::modeRead);
  506.     fileH.SafeOpen(strHFileName,
  507.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  508.     fileInl.SafeOpen(strInlFileName,
  509.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  510.     fileCpp.SafeOpen(strCppFileName,
  511.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  512.  
  513.     // Push the command line onto the file buffer, so that we can parse it
  514.     // using our standard tool set.
  515.     //
  516.     fileCtt.InitBuf(stringTypes);
  517.  
  518.     int chToken;
  519.     CString astringTypeParam[istringTypeParamMax];
  520.     int istringTypeParam = 0;
  521.  
  522.     fileCtt.GetNameToken();
  523.     stringTemplateName = fileCtt.TokenString();
  524.  
  525.     chToken = fileCtt.GetPrintToken();
  526.     CheckSyntax(chToken == '<', "'<'");
  527.  
  528.     do
  529.     {
  530.         chToken = fileCtt.GetTypeToken();
  531.         astringTypeParam[istringTypeParam++] = fileCtt.TokenString();
  532.         CheckSyntax(istringTypeParam < istringTypeParamMax,
  533.             "fewer parameterized types (program limit)");
  534.     } while (chToken == ',');
  535.  
  536.     CheckSyntax(chToken == '>', "'>'");
  537.  
  538.     fileCtt.GetNameToken();
  539.     stringTypedefName = fileCtt.TokenString();
  540.  
  541.     map.SetAt(stringTemplate, "");
  542.     map.SetAt(stringTemplateName, stringTypedefName);
  543.  
  544.     // Done processing the command line part, now eat any initial comments
  545.     // appearing before the //$DECLARE_TEMPLATE flag.
  546.     //
  547.     while ((chToken = fileCtt.GetPrintToken()) != chMoreThanOne ||
  548.            fileCtt.TokenString() != "DECLARE_TEMPLATE")
  549.     {
  550.         /* spin */ ;
  551.     }
  552.  
  553.     while ((fileCtt.GetToken() != chMoreThanOne) ||
  554.            (fileCtt.TokenString() != stringTemplate))
  555.     {
  556.         fileH.PutToken();
  557.     }
  558.  
  559.     // Token must now be "template".
  560.  
  561.     // Eat opening '<'.
  562.     //
  563.     chToken = fileCtt.GetPrintToken();
  564.  
  565.     // Now get a list of type parameters.
  566.     //
  567.     int istringTypeParamMaxPrev = istringTypeParam;
  568.     istringTypeParam = 0;
  569.     while (chToken !='>')
  570.     {
  571.         CString stringParamName;
  572.  
  573.         // The parameter name is the last thing before a ',' or '>'.
  574.         //
  575.         while ((chToken = fileCtt.GetPrintToken()) != ',' && chToken != '>')
  576.         {
  577.             stringParamName = fileCtt.TokenString();
  578.         }
  579.  
  580.         map.SetAt(stringParamName, astringTypeParam[istringTypeParam++]);
  581.     }
  582.  
  583.     CheckSyntax(istringTypeParam == istringTypeParamMaxPrev,
  584.         "same number of template parameters");
  585.  
  586.     // Copy template to header file, translating words as necessary,
  587.     // terminating when the //$IMPLEMENT_TEMPLATE_INLINES flag is hit.
  588.     TranslateTo(fileH, "IMPLEMENT_TEMPLATE_INLINES");
  589.  
  590.     // Copy template to inline file, translating words as necessary,
  591.     // terminating when the //$IMPLEMENT_TEMPLATE flag is hit.
  592.     TranslateTo(fileInl, "IMPLEMENT_TEMPLATE");
  593.  
  594.     // Copy template to source file, translating words as necessary.
  595.     TranslateTo(fileCpp, "END_OF_FILE");
  596.  
  597.     return 0;
  598. }
  599.